//
// RequestHandler.cpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <fstream>
#include <sstream>
#include <string>
#include <iostream>

#include "RequestHandler.h"
#include "MimeTypes.h"
#include "Reply.h"
#include "Request.h"

namespace http
{
	namespace server
	{
		RequestHandler::RequestHandler(const std::string& doc_root)
			: doc_root_(doc_root)
		{
			serverUtil = ServerUtil::getInstance(); // get instance of server util
			auto host = serverUtil->getProperty("db_host");
			auto user_db = serverUtil->getProperty("db_user");
			auto db_pass = serverUtil->getProperty("db_password");
			auto port = serverUtil->getProperty("db_port");
			auto db = serverUtil->getProperty("db_name");
			connector.connect(host, user_db, db_pass, atoi(port.c_str()));
			connector.setSchema(db);
		}

		/******************************************************************
		************
		* ++
		* Method name 	: handleRequest
		* Description   : handle request send from client
		* Parameters    : req : request client sent
		*						   : rep : server respone
		*
		* Global Variables
		*    Accessed   : none
		*    Modified   : none
		*
		* Return values : void
		*
		* Modifications  : Endless love
		* Description
		*
		* --
		*******************************************************************
		**********/
		void RequestHandler::handle_request(const Request& req, Reply& rep)
		{
			// Decode url to path.
			std::string request_path;
			if (!url_decode(req.uri, request_path))
			{
				rep = Reply::stock_reply(Reply::bad_request);
				return;
			}

			// Request path must be absolute and not contain "..".
			if (request_path.empty() || request_path[0] != '/'
				|| request_path.find("..") != std::string::npos)
			{
				rep = Reply::stock_reply(Reply::bad_request);
				return;
			}

			// If path ends in slash (i.e. is a directory) then add "index.html".
			if (request_path[request_path.size() - 1] == '/')
			{
				request_path += "index.html";
			}

			// check login
			if (request_path == "/login")
			{
				// sample data: "user_name=admin;password=admin"
				auto param = req.headers[2].value;
				auto startUser = 10; // after ='
				auto endUser = param.find("password="); // before ';
				auto startPass = endUser + 9;
				auto user_length = endUser - startUser;
				auto user_name = param.substr(startUser, user_length);
				auto pass = param.substr(startPass);
				UserInfo user = UserInfo(user_name, pass);
				auto valid = serverUtil->isValidUser(connector, user); // check if user is valid
				rep.status = Reply::ok;
				rep.content = user.toXmlString();
				rep.headers.resize(2);
				rep.headers[0].name = "Content-Length";
				rep.headers[0].value = std::to_string(rep.content.size());
				rep.headers[1].name = "Content-Type";
				rep.headers[1].value = "data";
				// result ok;
				return;
			}
			else
			{
				if (request_path == "/data")
				{
					// get data id

				}
			}

			// Determine the file extension.
			std::size_t last_slash_pos = request_path.find_last_of("/");
			std::size_t last_dot_pos = request_path.find_last_of(".");
			std::string extension;
			if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
			{
				extension = request_path.substr(last_dot_pos + 1);
			}

			// Open the file to send back.
			std::string full_path = doc_root_ + request_path;
			std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
			if (!is)
			{
				rep = Reply::stock_reply(Reply::not_found);
				return;
			}

			// Fill out the reply to be sent to the client.
			rep.status = Reply::ok;
			char buf[512];
			rep.content = ""; // reset content to zero
			while (is.read(buf, sizeof(buf)).gcount() > 0)
			{
				rep.content.append(buf, is.gcount());
			}

			rep.headers.resize(2);
			rep.headers[0].name = "Content-Length";
			rep.headers[0].value = std::to_string(rep.content.size());
			rep.headers[1].name = "Content-Type";
			rep.headers[1].value = mime_types::extension_to_type(extension);
		}


		bool RequestHandler::url_decode(const std::string& in, std::string& out)
		{
			out.clear();
			out.reserve(in.size());
			for (std::size_t i = 0; i < in.size(); ++i)
			{
				if (in[i] == '%')
				{
					if (i + 3 <= in.size())
					{
						int value = 0;
						std::istringstream is(in.substr(i + 1, 2));
						if (is >> std::hex >> value)
						{
							out += static_cast<char>(value);
							i += 2;
						}
						else
						{
							return false;
						}
					}
					else
					{
						return false;
					}
				}
				else if (in[i] == '+')
				{
					out += ' ';
				}
				else
				{
					out += in[i];
				}
			}
			return true;
		}
	} // namespace server
} // namespace http
